#pragma once
#include <memory>
#include <string>
#include <iostream>
#include "sqlite_orm.h"
namespace Media{
using namespace sqlite_orm;

const int MediaVideo = 0;
const int MediaAudio = 1;
const int MediaAll = 2;

using MediaConnectType = std::string;

const MediaConnectType HDMI_TYPE = "hdmi";
const MediaConnectType USBA_TYPE = "usb-type-a";
const MediaConnectType USBC_TYPE = "usb-type-c";
const MediaConnectType LINEIN_TYPE = "line-in";
const MediaConnectType LINEOUT_TYPE = "line-out";
const MediaConnectType BLUETOOTH_TYPE = "Bluetooth";
const MediaConnectType NETWORK_TYPE = "network";
const MediaConnectType MIRROR_TYPE = "mirror";
const MediaConnectType LOCAL_FILE_TYPE = "localFile";
const MediaConnectType AIRPLAY_TYPE = "airPlay";
const MediaConnectType QUICKSHARE_TYPE = "quickShare";

struct inputSourceTable{
    uint32_t m_id;
    std::string m_name;
    int m_type;  // 0 video, 1 audio, 2 video_audio
    std::string m_con_type; //HDMI_TYPE USBA_TYPE LINEIN_TYPE ...
    std::string m_content;
    bool m_custom; 
};

struct outputSourceTable{
    uint32_t m_id;
    std::string m_name;
    int m_type; //0 video, 1 audio
    std::string m_con_type; //HDMI_TYPE USBA_TYPE LINEIN_TYPE ...
    std::string m_content;
    bool m_custom;
};

struct scenesItemTable{
    uint32_t m_id;
    uint32_t m_sourceId;
    uint32_t m_scenesId;
    //for video json {"x":0, "y":0, "width":1920, "height":1080, "layers":1}
    //for audio ...
    std::string m_itemDesc;
    int m_type; //0 video, 1 audio
};

struct scenesTable{
    uint32_t m_id{};
    std::string m_name;
    //json array {"resolution":{"width":1920, "height":1080}, "frameRate":30, "bitRate":"4096", "codec":"H264", "outputList":[{"outputId":2}, {"outputId":3}]}
    std::string m_videoOutputConfig;
    //json array {"sampleRate":44100, "channel":2, "codec":"AAC","outputList":[{"outputId":4}, {"outputId":5}]}
    std::string m_audioOutputConfig;
    bool m_enabled{};
};


class SqlTableOp{
public:
    using Ptr = std::shared_ptr<SqlTableOp>;

    #define CreateInputSourceTable (make_table("inputSource", \
                            make_column("id", &inputSourceTable::m_id, primary_key()),\
                            make_column("name", &inputSourceTable::m_name),\
                            make_column("media_type", &inputSourceTable::m_type),\
                            make_column("connect_type", &inputSourceTable::m_con_type),\
                            make_column("content", &inputSourceTable::m_content),\
                            make_column("custom", &inputSourceTable::m_custom)))

    #define CreateOutputSourceTable (make_table("outputSource",\
                            make_column("id", &outputSourceTable::m_id, primary_key()),\
                            make_column("name", &outputSourceTable::m_name),\
                            make_column("media_type", &outputSourceTable::m_type),\
                            make_column("connect_type", &outputSourceTable::m_con_type),\
                            make_column("content", &outputSourceTable::m_content),\
                            make_column("custom", &outputSourceTable::m_custom)))


    #define CreateItemTable (make_table("scenesItem",\
                            make_column("id", &scenesItemTable::m_id, primary_key()),\
                            make_column("scenesId", &scenesItemTable::m_scenesId),\
                            make_column("itemDesc", &scenesItemTable::m_itemDesc),\
                            make_column("item_type", &scenesItemTable::m_type),\
                            make_column("sourceId", &scenesItemTable::m_sourceId)))

    #define CreateScenesTable (make_table("scenes",\
                            make_column("id", &scenesTable::m_id, primary_key()),\
                            make_column("name", &scenesTable::m_name),\
                            make_column("videoOutputConfig", &scenesTable::m_videoOutputConfig),\
                            make_column("audioOutputConfig", &scenesTable::m_audioOutputConfig),\
                            make_column("enabled", &scenesTable::m_enabled)))

    using InputSourceTableType = decltype(CreateInputSourceTable);
    using OutputSourceTableType = decltype(CreateOutputSourceTable);
    using ItemTableType = decltype(CreateItemTable);
    using ScenesTableType = decltype(CreateScenesTable);
    
    static Ptr Instance();
    SqlTableOp();
    ~SqlTableOp();

    template<typename table>
    bool SelectItemsFromScenesId(uint32_t scenesId, std::vector<table>& tbs){
        try{
            tbs = m_sqlDriver->get_all<table>(where(is_equal(&table::m_scenesId, scenesId)));
        }catch(const std::system_error& e){
            std::cout << e.what() << std::endl;
            return false;
        }

        return true;
    }

    template<typename table>
    bool DeleteItemsFromScenesId(uint32_t scenesId){
         try{
            m_sqlDriver->remove<table>(where(is_equal(&table::m_scenesId, scenesId)));
        }catch(const std::system_error& e){
            std::cout << e.what() << std::endl;
            return false;
        }

        return true;
    }

    template<typename table>
    bool InsertTable(table t, uint32_t& id){
        uint32_t entry_id = 0;
        try{
            entry_id = m_sqlDriver->insert(t);
        }catch(const std::system_error& e){
            std::cout << e.what() << std::endl;
            return false;
        }
        id = entry_id;
        return true;
    }

    template<typename table>
    bool UpdateTable(table t){
        try{
            m_sqlDriver->replace(t);
        }catch(const std::system_error& e){
            std::cout << e.what() << std::endl;
            return false;
        }
        return true;
    }

    template<typename table>
    bool DelTable(uint32_t id){
        try{
            m_sqlDriver->remove<table>(id);
        }catch(const std::system_error& e){
            std::cout << e.what() << std::endl;
            return false;
        }
        return true;
    }

    template<typename table>
    bool GetAllTable(std::vector<table>& tables){
        try{
            tables = m_sqlDriver->get_all<table>();
        }catch(const std::system_error& e){
            std::cout << e.what() << std::endl;
            return false;
        }
        return true;
    }

    template<typename table>
    bool GetOneTable(uint32_t id, table& t){
        try{
            t = m_sqlDriver->get<table>(id);
        }catch(const std::system_error& e){
            std::cout << e.what() << std::endl;
            return false;
        }
        return true;
    }


    bool SetScenesState(uint32_t scenesId, bool state){
        try{
            // m_sqlDriver->update_all(sqlite_orm::set(c(&scenesTable::m_enabled) = true), where(c(&scenesTable::m_id) == 1));
            // m_sqlDriver->update_all(sqlite_orm::set(c(&scenesTable::m_enabled) = state), sqlite_orm::where(c(&scenesTable::m_id) == 1));
            // m_sqlDriver->update_all(sqlite_orm::set(c(&scenesTable::m_enabled) = state), where(is_equal(&table::m_scenesId, scenesId)));
        
            // m_sqlDriver->update_all(set(assign(&scenesTable::m_enabled, state)), where(is_equal(&scenesTable::m_id, scenesId)));

            scenesTable tb;
            if(GetOneTable<scenesTable>(scenesId, tb)){
                tb.m_enabled = state;
                return UpdateTable<scenesTable>(tb);
            }
            return false;
        }catch(const std::system_error& e){
            std::cout << e.what() << std::endl;
            return false;
        }
    }

private:
    bool createTables();
    bool init();

    std::string m_dbFullPath;
    std::shared_ptr<internal::storage_t<InputSourceTableType, OutputSourceTableType,\
        ItemTableType,ScenesTableType>> m_sqlDriver;
};

}